package cc.creativecomputing.resonate.simulation;

import java.util.ArrayList;
import java.util.List;

import cc.creativecomputing.CCApp;
import cc.creativecomputing.CCApplicationManager;
import cc.creativecomputing.control.CCControl;
import cc.creativecomputing.graphics.CCGraphics.CCBlendMode;
import cc.creativecomputing.graphics.shader.CCShaderBuffer;
import cc.creativecomputing.graphics.shader.imaging.filter.CCSobelFilter;
import cc.creativecomputing.graphics.texture.CCTexture.CCTextureTarget;
import cc.creativecomputing.graphics.texture.CCTexture2D;
import cc.creativecomputing.graphics.texture.CCTextureIO;
import cc.creativecomputing.math.CCVector2f;
import cc.creativecomputing.math.CCVector3f;
import cc.creativecomputing.simulation.particles.CCGPUParticleSuperEmitter;
import cc.creativecomputing.simulation.particles.CCGPUParticles;
import cc.creativecomputing.simulation.particles.CCGPUParticlesDensity;
import cc.creativecomputing.simulation.particles.constraints.CCGPUConstraint;
import cc.creativecomputing.simulation.particles.forces.CCGPUForce;
import cc.creativecomputing.simulation.particles.forces.CCGPUGravity;
import cc.creativecomputing.simulation.particles.forces.CCGPUTextureForceField;
import cc.creativecomputing.simulation.particles.forces.CCGPUViscousDrag;


public class DensityApp extends CCApp{

	@CCControl(name="density strength", min=-10, max=10)
	private void setDensityStrength (float theStrength) {
		//_cDensityForceStrength=1f;
		_myDensityForce.strength(theStrength);
	}
	
	@CCControl(name="viscosity", min=0, max=10)
	private void setViscosity (float theStrength) {
		//_cDensityForceStrength=1f;
		_myViscousDrag.strength(theStrength);
	}
	
	@CCControl(name="gradient field", min=0, max=40)
	private void setGradientField (float theStrength) {
		//_cDensityForceStrength=1f;
		_myGradientForce.strength(theStrength);
	}
	
	private CCGPUParticles _myParticles;
	private CCTexture2D _myEmitTexture;
	private CCTexture2D _myPotentialTexture;
	private CCShaderBuffer _myGradientBuffer;
	
	private ParticleSuperEmitter _myEmitter;
	private CCGPUParticlesDensity _myDensity;
	private CCSobelFilter _mySobelFilter;
	
	
	private CCGPUTextureForceField _myDensityForce;
	private CCGPUViscousDrag       _myViscousDrag;
	private CCGPUTextureForceField _myGradientForce;
	
	@Override 
	public void setup() {
		
		_myEmitTexture      = new CCTexture2D (CCTextureIO.newTextureData("emit02.png"), CCTextureTarget.TEXTURE_RECT);
		_myPotentialTexture = new CCTexture2D (CCTextureIO.newTextureData("heightmap.png"), CCTextureTarget.TEXTURE_RECT);
		_myGradientBuffer   = new CCShaderBuffer(width, height);
		
		List<CCGPUForce> myForces = new ArrayList<CCGPUForce>();
		List<CCGPUConstraint> myConstraints = new ArrayList<CCGPUConstraint>();
		
		_mySobelFilter = new CCSobelFilter(g, _myPotentialTexture);
		calcGradient();
		
		myForces.add (_myDensityForce = new CCGPUTextureForceField (_mySobelFilter.output(), 
										new CCVector2f(1,1),
							    	    new CCVector2f( width/2, height/2)));
		myForces.add (_myViscousDrag  = new CCGPUViscousDrag(1));
		myForces.add (_myGradientForce = new CCGPUTextureForceField(_myGradientBuffer.attachment(0), new CCVector2f(1,1), new CCVector2f(width/2,height/2)));
		
		_myParticles = new CCGPUParticles(g, myForces, myConstraints, 1000, 1000);
		_myParticles.addEmitter (_myEmitter = new ParticleSuperEmitter(_myParticles, 0, 1000*1000));
		
		_myDensity = new CCGPUParticlesDensity(_myParticles, g, width, height);
		_mySobelFilter.setInput(_myDensity.getDensity());
		
		_myEmitter.setEmitTextures(_myEmitTexture, _myDensity.getDensity());
		_myDensityForce.strength(10);
		
		addControls("emit", "emit", _myEmitter);
		addControls("density", "density", _myDensity);
		addControls("forces", "forces", this);
	}
	
	public void calcGradient() {
		_mySobelFilter.update(0);
		_myGradientBuffer.beginDraw();
		g.clear();
		g.image(_mySobelFilter.output(), 0,0);
		_myGradientBuffer.endDraw();
	}
	@Override 
	public void update (float theDeltaTime) {
		_myParticles.update(theDeltaTime);
		_myDensity.update(theDeltaTime);
		_mySobelFilter.update(theDeltaTime);
	}
	
	@Override
	public void draw() {
		g.clear();
		_myParticles.draw();
		//g.blend(CCBlendMode.ADD);
		//g.image (_myDensity.getDensity(), -width/2, -height/2);
		//g.image (_myGradientBuffer.attachment(0), -width/2, -height/2);
		//g.image (_mySobelFilter.output(), -width/2, -height/2);
		//g.noBlend();
	}
	
	public static void main (String[] args) {
		CCApplicationManager myManager = new CCApplicationManager(DensityApp.class);
		myManager.settings().size(1000, 1000);
		myManager.settings().display(0);
		myManager.settings().fov(30);
		myManager.settings().antialiasing(4);
		myManager.start();
	}
}
